#include "FastbootThread.h"
#include <QFile>
#include <QMutexLocker>
extern"C" {
#include "fastboot.h"
}

FastbootThread::FastbootThread(QObject *parent) :
    QThread(parent), status(Idle), usb(NULL), vendorId(0)
{
}

void FastbootThread::clearOperation()
{
    locker.lock();
    opList.clear();
    locker.unlock();
}

void FastbootThread::appendOperation(const FastbootOperation &opCmd)
{
    locker.lock();
    opList.append(opCmd);
    locker.unlock();
}

bool FastbootThread::getOperation(FastbootOperation &opCmd)
{
    QMutexLocker l(&locker);
    if(opList.isEmpty())
        return false;
    opCmd = opList.takeFirst();
    return true;
}

void FastbootThread::run()
{
    while(status != Exit)
    {
        FastbootOperation op;
        if(getOperation(op))
        {
            status = Busy;
            usb_handle *usb = usb_open(match_fastboot, &op.serialNumber);
            if(usb != NULL)
            {
                emit logMessage(tr("Ready to process..."));
                switch(op.opType)
                {
                case FastbootOperation::WriteOperation:
                    emit logMessage(tr("----------------"));
                    emit logMessage(tr("Ready to write to %1")
                                    .arg(op.partName));
                    do {
                        QByteArray fileContent;
                        QFile fi(op.filePath);
                        if(!fi.open(QIODevice::ReadOnly))
                        {
                            emit logMessage(tr("Can't open image file.")
                                            + tr("Operation canceled"));
                            break;
                        }
                        emit logMessage(tr("Start to read image file..."));
                        while(!fi.atEnd() && (status != Canceled))
                        {
                            fileContent.append(fi.read(1024*1024));
                            emit logMessage(tr("%1...")
                                            .arg(fileContent.size()));
                        }
                        fi.close();
                        QByteArray partName = op.partName.toLatin1();
                        partName = "flash:" + partName;
                        if(status == Canceled)
                            break;
                        int s;
                        emit logMessage(tr("Downloading data..."));
                        // fb_queue_flash();
                        s = fb_download_data(usb,
                                                  fileContent.constData(),
                                                  fileContent.size());
                        if(s != 0)
                        {
                            emit logMessage(tr("Download FAILED!"));
                            break;
                        }
                        if(status == Canceled)
                            break;
                        emit logMessage(tr("Writing to flash..."));
                        s = fb_command(usb, partName.constData());
                        if(s != 0)
                            emit logMessage(tr("Write FAILED!"));
                        else
                            emit logMessage(tr("Write DONE!"));
                    } while(0);
                    break;
                case FastbootOperation::WrapOperation:
                    do {
                        QByteArray partName = op.partName.toLatin1();
                        partName = "erase:" + partName;
                        emit logMessage(tr("Erasing %1...").arg(op.partName));
                        int s = fb_command(usb, partName.constData());
                        if(s != 0)
                            emit logMessage(tr("Erase FAILED!"));
                        else
                            emit logMessage(tr("Erase DONE!"));
                    } while(0);
                    break;
                case FastbootOperation::RebootOperation:
                    do {
                        emit logMessage(tr("Rebooting device..."));
                        int s = fb_command(usb, "reboot");
                        if(s != 0)
                            emit logMessage(tr("Reboot FAILED!"));
                        else
                            emit logMessage(tr("Reboot DONE!"));
                    } while(0);
                    break;
                case FastbootOperation::RunCommandOperation:
                    do {
                        emit logMessage(tr("Run Command:"));
                        QByteArray cmd = "runcmd:" + op.partName.toLatin1();
                        char response[65] = "";
                        int s = fb_command_response(usb, cmd.constData(), response);
                        emit logMessage(tr("Response:%1").arg(response));
                    } while(0);
                    break;
                }

                usb_close(usb);
            }
            else
                emit logMessage(tr("Can't open device."));
            continue;
        }
        if(status == Busy || status == Canceled)
        {
            emit logMessage(tr("All Done!"));
            emit operationFinished();
            status = Idle;
        }
        QStringList list;
        usb_open(list_devices_callback, &list);
        if(list != devList)
        {
            devList = list;
            emit deviceListChanged(devList);
        }
        QThread::msleep(10);
    }
}

void FastbootThread::stop()
{
    status = Exit;
    wait();
}

void FastbootThread::writeImage(const QString &serialNumber,
                                const QString &partName,
                                const QString &filePath)
{
    if(partName.isEmpty() || filePath.isEmpty())
        return;
    FastbootOperation op;
    op.opType = FastbootOperation::WriteOperation;
    op.serialNumber = serialNumber;
    op.partName = partName;
    op.filePath = filePath;
    appendOperation(op);
}

void FastbootThread::wrapSection(const QString &serialNumber,
                                 const QString &partName)
{
    if(partName.isEmpty())
        return;
    FastbootOperation op;
    op.opType = FastbootOperation::WrapOperation;
    op.serialNumber = serialNumber;
    op.partName = partName;
    appendOperation(op);
}

void FastbootThread::runCommand(const QString &serialNumber,
                                const QString &cmd)
{
    if(cmd.isEmpty())
        return;
    FastbootOperation op;
    op.opType = FastbootOperation::RunCommandOperation;
    op.serialNumber = serialNumber;
    op.partName = cmd;
    appendOperation(op);
}

void FastbootThread::cancelAndClearOperation()
{
    clearOperation();
    status = Canceled;
    while(status != Idle && status != Exit)
        wait(100);
}

void FastbootThread::rebootDevice(const QString &serialNumber)
{
    FastbootOperation op;
    op.opType = FastbootOperation::RebootOperation;
    op.serialNumber = serialNumber;
    appendOperation(op);
}

int FastbootThread::match_fastboot(usb_ifc_info *info, void *arg)
{
    /*FastbootThread *This = (FastbootThread *)arg;*/
    if(/*!(This->vendorId && (info->dev_vendor == This->vendorId)) &&*/
       (info->dev_vendor != 0x18d1) &&  // Google
       (info->dev_vendor != 0x0451) &&
       (info->dev_vendor != 0x0502) &&
       (info->dev_vendor != 0x0fce) &&  // Sony Ericsson
       (info->dev_vendor != 0x05c6) &&  // Qualcomm
       (info->dev_vendor != 0x22b8) &&  // Motorola
       (info->dev_vendor != 0x0955) &&  // Nvidia
       (info->dev_vendor != 0x413c) &&  // DELL
       (info->dev_vendor != 0x0bb4))    // HTC
            return -1;
    if(info->ifc_class != 0xff) return -1;
    if(info->ifc_subclass != 0x42) return -1;
    if(info->ifc_protocol != 0x03) return -1;
    // require matching serial number if a serial number is specified
    // at the command line with the -s option.
    if(arg != NULL)
    {
        const QString *s = (const QString *)arg;
        if((!s->isEmpty()) && (*s != info->serial_number))
            return -1;
    }
    /*if ((!This->serialNumber.isEmpty())
            && strcmp(This->serialNumber.constData(), info->serial_number))
        return -1;*/
    return 0;
}

int FastbootThread::list_devices_callback(usb_ifc_info *info, void *arg)
{
    QStringList *list = (QStringList *)arg;
    do {
        if(match_fastboot(info, NULL))
            break;
        if(!info->writable)
            break;
        if(info->serial_number[0] == '\0')
            break;
        list->append(info->serial_number);
    } while(0);
    return -1;
}
